package org.marketcetera.modules.remote.receiver;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.Arrays;
import javax.security.auth.callback.*;
import javax.security.auth.login.AccountNotFoundException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.apache.commons.lang.ObjectUtils;
import org.apache.log4j.Level;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.marketcetera.client.ClientInitException;
import org.marketcetera.core.ClassVersion;
import org.marketcetera.core.LoggerConfiguration;
import org.marketcetera.module.ExpectedFailure;
import org.marketcetera.util.log.I18NBoundMessage;
import org.marketcetera.util.log.I18NMessage0P;
import org.marketcetera.util.misc.RandomStrings;
import org.marketcetera.util.test.TestCaseBase;
import com.sun.security.auth.UserPrincipal;
/* $License$ */
/**
* Tests {@link ClientLoginModule} functionality
*
* @author anshul@marketcetera.com
* @version $Id: ClientLoginModuleTest.java 16841 2014-02-20 19:59:04Z colin $
* @since 1.5.0
*/
@ClassVersion("$Id: ClientLoginModuleTest.java 16841 2014-02-20 19:59:04Z colin $") //$NON-NLS-1$
public class ClientLoginModuleTest extends TestCaseBase {
/**
* Test login success & failures.
* @throws Exception if there was failure
*/
@Test
public void loginTest() throws Exception {
setLevel(ClientLoginModule.class.getName(), Level.INFO);
//test failure conditions
attemptLogin(null, getTestPassword(),
AccountNotFoundException.class,
Messages.EMPTY_USERNAME.getText());
attemptLogin("", getTestPassword(),
AccountNotFoundException.class,
Messages.EMPTY_USERNAME.getText());
final String u = randomString();
attemptLogin(u, getTestPassword(),
FailedLoginException.class,
Messages.USER_LOGIN_FAIL.getText(u));
assertLastEvent(Level.WARN, ClientLoginModule.class.getName(),
Messages.USER_LOGIN_ERROR_LOG.getText(u),
ClientLoginModule.class.getName());
attemptLogin(getTestUsername(), null,
FailedLoginException.class,
Messages.USER_LOGIN_FAIL.getText(getTestUsername()));
assertLastEvent(Level.WARN, ClientLoginModule.class.getName(),
Messages.USER_LOGIN_ERROR_LOG.getText(getTestUsername()),
ClientLoginModule.class.getName());
attemptLogin(getTestUsername(), "".toCharArray(),
FailedLoginException.class,
Messages.USER_LOGIN_FAIL.getText(getTestUsername()));
assertLastEvent(Level.WARN, ClientLoginModule.class.getName(),
Messages.USER_LOGIN_ERROR_LOG.getText(getTestUsername()),
ClientLoginModule.class.getName());
attemptLogin(getTestUsername(), randomString().toCharArray(),
FailedLoginException.class,
Messages.USER_LOGIN_FAIL.getText(getTestUsername()));
assertLastEvent(Level.WARN, ClientLoginModule.class.getName(),
Messages.USER_LOGIN_ERROR_LOG.getText(getTestUsername()),
ClientLoginModule.class.getName());
//test failure due to client error
I18NMessage0P fail = new I18NMessage0P(Messages.LOGGER, "testMessage");
sMockHelper.setFail(fail);
attemptLogin(getTestUsername(), getTestPassword(),
FailedLoginException.class,
Messages.USER_LOGIN_ERROR.getText());
assertLastEvent(Level.WARN, ClientLoginModule.class.getName(),
Messages.USER_LOGIN_ERROR_LOG.getText(getTestUsername()),
ClientLoginModule.class.getName());
//test successful login
sMockHelper.setFail(null);
attemptLogin(getTestUsername(), getTestPassword(), null, null);
assertLastEvent(Level.INFO, ClientLoginModule.class.getName(),
Messages.USER_LOGIN_LOG.getText(getTestUsername()),
ClientLoginModule.class.getName());
// test logout removes the principal from the subject
loginContext.logout();
assertTrue(loginContext.getSubject().getPrincipals().isEmpty());
assertLastEvent(Level.INFO, ClientLoginModule.class.getName(),
Messages.USER_LOGOUT_LOG.getText(getTestUsername()),
ClientLoginModule.class.getName());
}
/**
* test unsupported callbacks
* @throws Exception if there was failure
*/
@Test
public void unsupportedCallback() throws Exception {
doNotHandleCallbacks = true;
UnsupportedCallbackException uce = new UnsupportedCallbackException(
new NameCallback(Messages.PROMPT_USERNAME.getText()));
LoginException ex = attemptLogin(getTestUsername(), getTestPassword(),
LoginException.class, uce.getMessage());
assertNotNull(ex.getCause());
assertTrue(ex.getCause() instanceof UnsupportedCallbackException);
Callback callback = ((UnsupportedCallbackException) ex.getCause()).getCallback();
assertNotNull(callback);
assertTrue(callback.getClass().toString(),
callback instanceof NameCallback);
org.junit.Assert.assertEquals(Messages.PROMPT_USERNAME.getText(),
((NameCallback)callback).getPrompt());
}
/**
* test callback io failure
* @throws Exception if there was a failure
*/
@Test
public void callbackIOFailure() throws Exception {
callbackException = new IOException("ioeoeoe"); //$NON-NLS-1$
LoginException ex = attemptLogin(getTestUsername(), getTestPassword(),
LoginException.class, callbackException.getMessage());
assertNotNull(ex.getCause());
assertTrue(ex.getCause() instanceof IOException);
assertSame(callbackException, ex.getCause());
}
/**
* Verify that the local helper cannot be set to null.
*
* @throws Exception if there were errors.
*/
@Test
public void nullHelperFail() throws Exception {
new ExpectedFailure<NullPointerException>(){
protected void run() throws Exception {
ClientLoginHelper.setCurrentHelper(null);
}
};
}
@BeforeClass
public static void setup() throws Exception {
LoggerConfiguration.logSetup();
//Set up our configuration.
JaasConfiguration.setup();
//Override login helper to help with unit testing
sMockHelper = new MockLoginHelper();
ClientLoginHelper.setCurrentHelper(sMockHelper);
}
@AfterClass
public static void cleanup() throws Exception {
ClientLoginHelper.setCurrentHelper(ClientLoginHelper.DEFAULT_HELPER);
}
@After
public void reset() throws Exception {
doNotHandleCallbacks = false;
callbackException = null;
sMockHelper.setFail(null);
}
private static String getTestUsername() {
return sMockHelper.getTestUsername();
}
private static char[] getTestPassword() {
return sMockHelper.getTestPassword();
}
private String randomString() {
return RandomStrings.genStrLetter();
}
/**
* Attempt login and test for failure / success conditions
*
* @param name the user name
* @param password the password
* @param failure expected failure
* @param failureMsg expected failure message
*
* @return the failure exception if any
*
* @throws Exception if there was unexpected failure
*/
private LoginException attemptLogin(
String name, char[] password,
Class<? extends LoginException> failure,
String failureMsg) throws Exception{
MockCallbackHandler ch = null;
loginContext = null;
try {
ch = new MockCallbackHandler(name, password);
loginContext = new LoginContext(
JaasConfiguration.REMOTING_LOGIN_DOMAIN,ch);
loginContext.login();
assertNull("Expected failure:" + failure + failureMsg, failure);
//verify that the appropriate principals are set in the subject
assertTrue(loginContext.getSubject().getPrincipals().toString(),
loginContext.getSubject().getPrincipals().contains(
new UserPrincipal(getTestUsername())));
} catch (LoginException e) {
assertNotNull("Unexpected failure:" + e,failure);
assertTrue("Expected:" + failure + ":Actual:" +
e.getClass().getName() + e.toString(),
failure.isInstance(e));
if (failureMsg != null) {
assertEquals(failureMsg,e.getMessage());
}
assertNotNull(loginContext);
//verify that the appropriate principals are not set in the subject
if (loginContext.getSubject() != null &&
loginContext.getSubject().getPrincipals() != null) {
assertFalse(loginContext.getSubject().getPrincipals().toString(),
loginContext.getSubject().getPrincipals().contains(
new UserPrincipal(getTestUsername())));
}
assertEquals(2,ch.getNumCallbacks());
//These values are only set if call back handler doesn't throw
//exceptions
if (callbackException == null && !doNotHandleCallbacks) {
assertEquals(Messages.PROMPT_USERNAME.getText(), ch.getNamePrompt());
assertEquals(Messages.PROMPT_PASSWORD.getText(), ch.getPasswordPrompt());
assertNull(ch.getDefaultName());
}
return e;
}
return null;
}
private static boolean doNotHandleCallbacks = false;
private static IOException callbackException = null;
private LoginContext loginContext;
private static MockLoginHelper sMockHelper;
/**
* A mock login helper to aid unit testing.
*/
private static class MockLoginHelper extends ClientLoginHelper {
@Override
protected boolean validateCredentials(String inUsername,
char[] inPassword)
throws ClientInitException {
if(mFail != null) {
throw new ClientInitException(mFail);
}
return ObjectUtils.equals(inUsername, mUsername) &&
Arrays.equals(inPassword, mPassword);
}
public String getTestUsername() {
return mUsername;
}
public char[] getTestPassword() {
return mPassword;
}
public void setFail(I18NBoundMessage inFailMessage) {
mFail = inFailMessage;
}
private I18NBoundMessage mFail = null;
private final String mUsername = "testuser";
private final char[] mPassword = "testpassword".toCharArray();
}
/**
* Test call back handler
*/
private static class MockCallbackHandler implements CallbackHandler {
MockCallbackHandler(String nameValue, char[] passwordValue) {
this.nameValue = nameValue;
this.passwordValue = passwordValue;
}
public String getNamePrompt() {
return namePrompt;
}
public String getPasswordPrompt() {
return passwordPrompt;
}
public String getDefaultName() {
return defaultName;
}
public int getNumCallbacks() {
return numCallbacks;
}
public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
if(callbacks == null) {
return;
}
numCallbacks = callbacks.length;
for(Callback c :callbacks) {
if(doNotHandleCallbacks) {
throw new UnsupportedCallbackException(c);
}
if(callbackException != null) {
throw callbackException;
}
if(c instanceof NameCallback) {
NameCallback nc = (NameCallback) c;
namePrompt = nc.getPrompt();
defaultName = nc.getName();
nc.setName(nameValue);
} else if (c instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback) c;
passwordPrompt = pc.getPrompt();
pc.setPassword(passwordValue);
} else {
throw new UnsupportedCallbackException(c);
}
}
}
private int numCallbacks;
private String namePrompt;
private String defaultName;
private String nameValue;
private String passwordPrompt;
private char[] passwordValue;
}
}